
//
// Created by ttao on 2022/7/25.
//

#include "dts.h"

static inline size_t _hdrsize(char type){
    switch (type&DTS_FLAG_MASK) {
        case DTS_FLAG_8:
            return sizeof(struct dts8);
        case DTS_FLAG_16:
            return sizeof(struct dts16);
        case DTS_FLAG_32:
            return sizeof(struct dts32);
        case DTS_FLAG_64:
            return sizeof(struct dts64);
    }
    return 0;
}

/*在64位系统中LONG_MAX 与 LLONG_MAX都占8个字节*/
/*在32位系统中LONG_MAX占4个字节，LLONG_MAX占8个字节*/
static inline char _reqtype(size_t stringlen)
//判断一个字符长度适合哪种数据格式
{
    if(1<<8 > stringlen)
        return DTS_FLAG_8;
    if(1<<16 > stringlen)
        return DTS_FLAG_16;
#if (LONG_MAX == LLONG_MAX)
    if(1ll<<32 > stringlen)
        return DTS_FLAG_32;
    return DTS_FLAG_64;
#else
    return DTS_FLAG_32;
#endif
}

static inline size_t _maxlen(char type)
{
    switch (type&DTS_FLAG_MASK) {
        case DTS_FLAG_8:
            return (1 << 8) - 1;
        case DTS_FLAG_16:
            return (1 << 16) - 1;
#if (LONG_MAX == LLONG_MAX)
        case DTS_FLAG_32:
            return (1ll << 32) - 1;
#else
            return -1;
#endif
    }
}

static inline size_t hdrsize(char type)
{
    return _hdrsize(type);
}

static inline char reqtype(size_t stringlen)
{
    return _reqtype(stringlen);
}

static inline size_t maxlen(char type)
{
    return _maxlen(type);
}

dts _dtsnewlen(const char *init, size_t initlen, int trymalloc)
{
    void *sh;
    dts d;
    char type= reqtype(initlen);
    int hdrlen = hdrsize(type);
    unsigned char *fp;      /*指向数据类型*/
    size_t usable;
    assert(initlen + hdrlen + 1 > initlen);
    sh = trymalloc?                             /*sh 指向数据体*/
            d_trymalloc_usable(hdrlen+initlen+1, &usable):
            d_malloc_usable(hdrlen+initlen+1, &usable);
    if(sh == NULL) return NULL;
    memset(sh, 0, hdrlen+initlen+1);
    d = (char *) sh + hdrlen;
    fp = ((unsigned char*)d) - 1;
    usable = usable - hdrlen - 1;
    if(usable > maxlen(type))
        usable = maxlen(type);
    switch (type) {
        case DTS_FLAG_8:
        {
            DTS_VAR(8, d);
            dt->len = initlen;
            dt->alloc = usable;
            *fp = type;
            break;
        }
        case DTS_FLAG_16:
        {
            DTS_VAR(16, d);
            dt->len = initlen;
            dt->alloc = usable;
            *fp = type;
            break;
        }
        case DTS_FLAG_32:
        {
            DTS_VAR(32, d);
            dt->len = initlen;
            dt->alloc = usable;
            *fp = type;
            break;
        }
        case DTS_FLAG_64:
        {
            DTS_VAR(64, d);
            dt->len = initlen;
            dt->alloc = usable;
            *fp = type;
            break;
        }
    }
    if(initlen && init)
        memcpy(d, init, initlen);
    d[initlen] = '\0';
    return d;
}
static inline dts _dtsresizelen(char* ptr, const size_t size)
{
    char newtype, oldtype = ptr[-1] & DTS_FLAG_MASK;
    int newhdrlen, oldhdrlen = hdrsize(oldtype);
    dts newsh,oldsh = ptr - oldhdrlen;
    size_t oldlen = dtslen(ptr);
    if(dtsalloc(ptr) == size) return ptr;   // 已经满足
    if(oldlen > size) oldlen = size;
    newtype = reqtype(size);
    newhdrlen = hdrsize(newtype);

    if(oldtype == newtype || (newtype < oldtype && newtype > DTS_FLAG_8))
    {
        newsh = d_realloc(oldsh, oldhdrlen + size + 1);
        if(newsh == NULL) return NULL;
        ptr = newsh + newhdrlen;
        ptr[-1] = newtype;
    } else {
        newsh = tmalloc(size);
        if(newsh == NULL) return NULL;
        memcpy((char*)newsh + newhdrlen, ptr, oldlen);
        d_free(ptr-oldhdrlen);
        ptr = (char*) newsh + newhdrlen;
        ptr[-1] = newtype;
    }
    ptr[oldlen] = '\0';
    setdtslen(ptr, oldlen);
    setdtsalloc(ptr, size);
    return ptr;
}
static inline dts _dtsmakesizefor(dts d, size_t addlen, int greedy)
// 扩展dts的字符串长度
{
    void *sh,*newsh;
    size_t avail = dtsavail(d);
    size_t len ,newlen, reqlen;
    char type, oldtype = d[-1] & DTS_FLAG_MASK;
    int hdrlen;
    size_t usable;

    if(avail >= addlen) return d;   // 有足够的空间时退出

    len = dtslen(d);
    sh = (char *)d-hdrsize(oldtype);
    reqlen = newlen = (len+addlen);
    assert(newlen > len);
    if(greedy == 1){
        if(newlen < DTS_MAX_PREALLOC)
            newlen *= 2;
        else
            newlen += DTS_MAX_PREALLOC;
    }

    type = reqtype(newlen);

    hdrlen = hdrsize(type);
    assert(hdrlen + newlen + 1 >  reqlen);      //防止数据溢出
    if(oldtype==type){
        newsh = d_realloc_usable(sh, hdrlen+newlen+1, &usable);
        if(newlen == 0) return NULL;
        d = (char*)newsh+hdrlen;
    } else {
        newsh = d_malloc_usable(hdrlen+newlen+1, &usable);
        if(newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, d, len+1);
        d_free(sh);
        d = (char *)newsh + hdrlen;
        d[-1] = type;
        setdtslen(d, len);
    }
    usable = usable-hdrlen-1;
    if(usable > maxlen(type))
        usable = maxlen(type);
    setdtsalloc(d, usable);
    return d;
}
/* Example:
 *
 * s = dtsrange("Hello World");
 * dtsrange(s,1,-1); => "ello World"
 */
int dtsrange(dts s, size_t start, size_t end) {
    size_t newlen, len = dtslen(s);
    if (len > SSIZE_MAX) return -1;

    if (len == 0) return 0;
    if (start < 0) {
        start = len+start;
        if (start < 0) start = 0;
    }
    if (end < 0) {
        end = len+end;
        if (end < 0) end = 0;
    }
    newlen = (start > end) ? 0 : (end-start)+1;
    if (newlen != 0) {
        if (start >= (size_t)len) {
            newlen = 0;
        } else if (end >= (size_t)len) {
            end = len-1;
            newlen = (start > end) ? 0 : (end-start)+1;
        }
    } else {
        start = 0;
    }
    if (start && newlen) memmove(s, s+start, newlen);
    s[newlen] = 0;
    setdtslen(s,newlen);
    return 0;
}
static inline dts dtsmakesizeforgreedy(dts d, size_t addlen) {
    return _dtsmakesizefor(d, addlen, 1);
}

static inline dts dtsmakesizefornongreedy(dts d, size_t addlen) {
    return _dtsmakesizefor(d, addlen, 0);
}
static inline dts dtsresizelen(dts ptr, const size_t size)
{
    return _dtsresizelen(ptr, size);
}

dts dtsnewlen(const char *init, size_t initlen)
{
    return _dtsnewlen(init, initlen, 0);
}

dts dtstrynewlen(const char *init, size_t initlen)
{
    return _dtsnewlen(init, initlen, 1);
}

dts dtsnew(const char * init)
{
    size_t initlen = (init == NULL)? 0 : strlen(init);
    return dtsnewlen(init, initlen);
}

dts dtsresize(dts ptr, const size_t size)
{
    if(!ptr) return NULL;
    if(size <= 0)
    {
        dtsfree(ptr);
        return NULL;
    }
    return dtsresizelen(ptr, size);
}
static inline dts sdscpylen(dts d, const char *t, size_t len) {
    if (dtsalloc(d) < len) {
        d = dtsmakesizeforgreedy(d,len-dtslen(d));
        if (d == NULL) return NULL;
    }
    memcpy(d, t, len);
    d[len] = '\0';
    setdtslen(d, len);
    return d;
}
dts dtscpystr(dts d, const char *t){
    return sdscpylen(d, t, strlen(t));
}

dts dtscpy(dts desc, dts sre){
    return dtscpystr(desc, sre);
}
// clone
dts dtsdup(const dts s) {
    return dtsnewlen(s, dtslen(s));
}
static inline dts  dtscatlen(dts s, const void *t, size_t len) {
    size_t curlen = dtslen(s);

    s = dtsmakesizeforgreedy(s,len);
    if (s == NULL) return NULL;
    memcpy(s+curlen, t, len);
    setdtslen(s, curlen+len);
    s[curlen+len] = '\0';
    return s;
}
// 追加字符串
dts dtscat(dts s, const char *t) {
    return dtscatlen(s, t, strlen(t));
}

//合并dts
dts dtscatsds(dts s, const dts t) {
    return dtscatlen(s, t, dtslen(t));
}
// 创建一个空dts
dts dtsempty(void) {
    return dtsnewlen("",0);
}
// 清空dts
void dtsclear(dts s) {
    dtsrange(s, 1, 0);
}
/*
 * 清空自定字符串
 * Example:
 * s = dtstrim("AA...AA.a.aa.aHelloWorld     :::");
 * s = dtstrim(s,"Aa. :");
 * printf("%s\n", s);
 *
 * Output will be just "Hello World".
 */
dts dtstrim(dts s, const char *cset) {
    char *start, *end, *sp, *ep;
    size_t len;

    sp = start = s;
    ep = end = s+dtslen(s)-1;
    while(sp <= end && strchr(cset, *sp)) sp++;
    while(ep > sp && strchr(cset, *ep)) ep--;
    len = (sp > ep) ? 0 : ((ep-sp)+1);
    if (s != sp) memmove(s, sp, len);
    s[len] = '\0';
    setdtslen(s,len);
    return s;
}

/* Compare two hisds strings s1 and s2 with memcmp().
 *
 * Return value:
 *
 *     positive if s1 > s2.
 *     negative if s1 < s2.
 *     0 if s1 and s2 are exactly the same binary string.
 *
 * If two strings share exactly the same prefix, but one of the two has
 * additional characters, the longer string is considered to be greater than
 * the smaller one. */
int dtscmp(const dts s1, const dts s2) {
    size_t l1, l2, minlen;
    int cmp;

    l1 = dtslen(s1);
    l2 = dtslen(s2);
    minlen = (l1 < l2) ? l1 : l2;
    cmp = memcmp(s1,s2,minlen);
    if (cmp == 0) return l1-l2;
    return cmp;
}
void dtstoupper(dts s) {
    int len = dtslen(s), j;

    for (j = 0; j < len; j++) s[j] = toupper(s[j]);
}
void dtstolower(dts s) {
    int len = dtslen(s), j;

    for (j = 0; j < len; j++) s[j] = tolower(s[j]);
}
size_t dtsfree(dts d)
{
    if(d == NULL) return -1;
    return d_free((void *)(d) - hdrsize(d[-1]));
}
